package com.lagou.edu.factory;

import com.lagou.edu.annotation.*;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.*;

/**
 * @author 应癫
 *
 * 工厂类，生产对象（使用反射技术）
 */
@ComponentScan("com.lagou.edu")
public class BeanFactory {

    private static Map<String,Object> map = new HashMap<>();  // 存储对象
    private static Map<Class,Set<Object>> typeMap = new HashMap<>();  // 存储对象

    static {
        try {
            ComponentScan annotation = BeanFactory.class.getAnnotation(ComponentScan.class);
            String path = annotation.value().replaceAll("\\.", File.separator) + File.separator;
            Enumeration<URL> resourceUrls = Thread.currentThread().getContextClassLoader().getResources(path);
            URL url = resourceUrls.nextElement();
            File file = new File(new URI(url.toString()).getSchemeSpecificPart());

            // 初始化bean
            for (String fileName : getFiles(file)) {
                String packageName = fileName.substring(8 + fileName.lastIndexOf("classes"), fileName.indexOf(".class")).replaceAll(File.separator, ".");
                Class<?> aClass = Class.forName(packageName);

                Component component = aClass.getAnnotation(Component.class);
                if (Objects.nonNull(component)){
                    String beanName = component.value().isEmpty() ? getBeanName(aClass) : component.value();
                    doCreateBean(beanName,aClass);
                }
                Service service = aClass.getAnnotation(Service.class);
                if (Objects.nonNull(service)){
                    String beanName = service.value().isEmpty() ? getBeanName(aClass) : service.value();
                    doCreateBean(beanName,aClass);
                }
                Repository repository = aClass.getAnnotation(Repository.class);
                if (Objects.nonNull(repository)){
                    String beanName = repository.value().isEmpty() ? getBeanName(aClass) : repository.value();
                    doCreateBean(beanName,aClass);
                }
            }

            // autowired 注入
            for (Object value : map.values()) {
                Class<?> aClass = value.getClass();
                for (Field field : aClass.getDeclaredFields()) {
                    if (Objects.nonNull(field.getAnnotation(Autowired.class))){
                        field.setAccessible(true);
                        Set<Object> objects = typeMap.get(field.getType());
                        if (objects.size() > 1){
                            throw new RuntimeException("不能唯一确定bean");
                        }
                        field.set(value,objects.iterator().next());
                    }
                }
            }
            // 代理对象替换
            for (String beanName : map.keySet()){
                Object bean = map.get(beanName);
                Class<?> aClass = bean.getClass();
                if (Objects.nonNull(aClass.getAnnotation(Transactional.class))){
                    ProxyFactory proxyFactory = (ProxyFactory)doCreateBean("proxyFactory",ProxyFactory.class);
                    if (aClass.getInterfaces().length > 0){
                        map.put(beanName,proxyFactory.getJdkProxy(bean));
                    }else {
                        map.put(beanName,proxyFactory.getCglibProxy(bean));
                    }
                }
            }


        } catch (IOException | URISyntaxException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

    private static Object doCreateBean(String beanName, Class<?> aClass) throws InstantiationException, IllegalAccessException {
        Object newInstance = map.get(beanName);
        if (Objects.isNull(newInstance)){
            newInstance = aClass.newInstance();
        }
        map.put(beanName,newInstance);
        fillTypeMap(aClass, newInstance);
        return newInstance;
    }

    private static void fillTypeMap(Class<?> aClass, Object o) {
        Class<?>[] interfaces = aClass.getInterfaces();
        if (Objects.nonNull(interfaces) && interfaces.length > 0){
            for (Class<?> anInterface : interfaces) {
                Set<Object> objects = typeMap.getOrDefault(anInterface, new HashSet<>());
                objects.add(o);
                typeMap.put(anInterface,objects);
            }
        }else {
            Set<Object> objects = typeMap.getOrDefault(aClass, new HashSet<>());
            objects.add(o);
            typeMap.put(aClass,objects);
        }
    }

    private static String getBeanName(Class<?> aClass) {
        String beanName = aClass.getSimpleName();
        if (aClass.getSimpleName().charAt(0) < 'Z'){
            beanName = (char) (aClass.getSimpleName().charAt(0) + 32) +aClass.getSimpleName().substring(1);
        }
        return beanName;
    }

    private static List<String> getFiles(File file){
        if (Objects.isNull(file)){
            return new ArrayList<>();
        }
        List<String> result = new ArrayList<>();
        if (file.isDirectory()){
            File[] files = file.listFiles();
            if (Objects.nonNull(files)){
                for (File file1 : files) {
                    result.addAll(getFiles(file1));
                }
            }
        }else {
            if (file.getName().endsWith(".class")){
                result.add(file.getPath());
            }
        }
        return result;
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static  Object getBean(String id) {
        return map.get(id);
    }

}
